package socket.server_socket;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

public class Server {
    @Test
    public void demo_01() {
        try(ServerSocket server = new ServerSocket(1024)) {
            while(true) {
                Socket connection = server.accept();
                OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
                Date now = new Date();
                out.write(now.toString() + "\r\n");
                out.flush();
                connection.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 多线程服务器
     */
    @Test
    public void demo_02() {
        try(ServerSocket server = new ServerSocket()) {
            InetSocketAddress address = new InetSocketAddress(80);
            server.bind(address);
            Socket connection = server.accept();
            DayTimeThread task = new DayTimeThread(connection);
            task.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写入服务器
     *
     * RFC 862 定义的echo协议是最简单的TCP服务之一。客户端打开指向echo服务器端口的socket，并发所数据。服务器将数据发回。
     * 这个过程一直继续，直到客户端关闭连接为止。echo协议很有用，可以测试网络，确保数据没有被有问题的路由器和防火墙所破坏。
     * 可以使用Telnet实验echo
     */
    @Test
    public void demo_03() {
        ServerSocketChannel channel;
        Selector selector;
        try {
            channel = ServerSocketChannel.open();
            ServerSocket ss = channel.socket();
            InetSocketAddress address = new InetSocketAddress(7);
            ss.bind(address);
            channel.configureBlocking(false);
            selector = Selector.open();
            channel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        while(true) {
            try {
                selector.select();
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }

            Set<SelectionKey> readyKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while(iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                try {
                    if(key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel client = server.accept();
                        System.out.println("Accepted connection from " + client);
                        client.configureBlocking(false);
                        SelectionKey clientKey = client.register(
                                selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        clientKey.attach(buffer);
                    }

                    if(key.isReadable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        client.read(output);
                    }

                    if(key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        output.flip();
                        client.write(output);
                        output.compact();
                    }
                } catch (IOException e) {
                    key.channel();
                    try{
                        key.channel().close();
                    } catch (IOException e1) {

                    }
                }
            }

        }
    }
}

class DayTimeThread extends Thread {
    private Socket connection;

    public DayTimeThread(Socket connection) {
        this.connection = connection;
    }

    @Override
    public void run() {
        try {
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
            Date now = new Date();
            out.write(now.toString() + "\r\n");
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (IOException e) {

            }
        }
    }
}
